1. Field of the Invention
The present invention relates to computer software, and deals more particularly with methods, systems, computer program products, and methods of doing business whereby programmatically-generated byte code insertion is used to perform run-time tracing of code that waits (or may potentially wait).
2. Description of the Related Art
The Java™ programming language developed by Sun Microsystems, Inc. has gained wide acceptance for writing software for the Internet and World Wide Web. While compilers for most programming languages generate code for a particular operating environment, Java enables writing programs using a “write once, run anywhere” paradigm. (“Java” and “Write Once, Run Anywhere” are trademarks of Sun Microsystems, Inc.)
Java attains its portability through use of a specially-designed virtual machine (“VM”). This virtual machine is also referred to as a “Java Virtual Machine”, or “JVM”. The virtual machine enables isolating the details of the underlying hardware from the compiler used to compile the Java programming instructions. Those details are supplied by the implementation of the virtual machine, and include such things as whether little Endian or big Endian format is used for storing compiled instructions, and the length of an instruction once it is compiled. Because these machine-dependent details are not reflected in the compiled code, the code can be transported to a different environment (a different hardware machine, a different operating system, etc.), and executed in that environment without requiring the code to be changed or recompiled—hence the phrase “write once, run anywhere”. The compiled code, referred to as Java “byte code”, then runs on top of a JVM, where the JVM is tailored to that specific operating environment. As an example of this tailoring of the JVM, if the byte code is created using little Endian format but is to run on a microprocessor expecting big Endian, then the JVM would be responsible for converting the instructions from the byte code before passing them to the microprocessor.
Servers in which Java applications are hosted, commonly referred to as “Java application servers” (or simply “application servers”), may provide run-time services to a myriad of Java applications, and these applications may service requests for a large number of concurrent requesters (including other programs as well as human users). The performance requirements placed on these application servers by the deploying enterprise and/or its end users are typically quite demanding, and as the Java applications and deployment scenarios become increasingly complex, these performance requirements tend to increase as well.
The performance of a Java application can suffer if the application executes one or more types of “waits”. For example, applications often need to access a limited resource, such as by connecting to a database to retrieve or store data. The number of available connections to a database is often restricted to a configured maximum, and thus some of the concurrently executing threads may have to block until a database connection becomes available. In a web-centric environment, the incoming requests to an application server may be very homogeneous, such that many requests attempt to exercise the same or similar execution paths. Thus, blockages on commonly-needed critical resources may occur relatively often, and these blockages can have an adverse effect on many users. In such situations, contention for the resources frequently becomes a major source of bottlenecks in application servers.
More generally, whenever an application is forced to wait, performance suffers. In many cases, the applications make use of various types of synchronization that is facilitated by Java monitors. Synchronization can be used to protect shared resources (e.g., by serializing access to a resource) or to enforce an ordering of execution (e.g., by using event handling, where one thread waits for another thread to signal, or “notify”, that it has reached a particular point; this is particularly common for synchronizing operation among producer threads and consumer threads). However, synchronization generally reduces concurrency, and if the applications executing on a particular application server are encountering a large number of waits and/or are waiting for relatively long periods of time, performance will be degraded.
It is therefore advantageous to determine how a particular application, or perhaps a plurality of applications, executing on an application server is/are waiting in a production environment. Techniques are known in the art for observing application performance characteristics using tools such as the Java Virtual Machine Profiling Interface (“JVMPI”), which allows peeking into the operation of a JVM. However, the JVMPI is a relatively heavyweight approach that has drawbacks. For example, for many JVMs, just-in-time compilation (commonly referred to as “JIT”) must be turned off when the JVMPI is enabled. This typically alters the performance aspects of the system being analyzed, making the gathered information somewhat unreliable as a predictor of true run-time behavior.
Accordingly, a need exists for overcoming these shortcomings of the prior art.